package com.daffodil.core.dao.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.daffodil.core.dao.JdbcDao;
import com.daffodil.core.dao.helper.DaoHelper;
import com.daffodil.core.entity.KeyFormatter;
import com.daffodil.core.entity.Page;
import com.daffodil.core.exception.BusinessException;

/**
 * 
 * @author yweijian
 * @date 2022年3月3日
 * @version 1.0
 * @description
 */
@Repository
public class JdbcDaoImpl implements JdbcDao {

    private static final int PAGE_MAX_DEPTH = 1000;

    @Autowired
    private EntityManager entityManager;

    public void execute(String sql) throws BusinessException {
        this.execute(sql, null);
    }

    public void execute(String sql, Object para) throws BusinessException {
        List<Object> paras = new ArrayList<Object>();
        paras.add(para);
        this.execute(sql, paras);
    }

    public void execute(String sql, List<Object> paras) throws BusinessException {
        try {
            String querySql = DaoHelper.getQuerySql(sql);
            Query query = entityManager.createNativeQuery(querySql);
            DaoHelper.setQueryParas(query, paras);
            query.executeUpdate();
        } catch (Exception e) {
            throw new BusinessException("[JDBC] Error in executing SQL statement ...", e);
        } 
    }

    public List<Map<String, String>> search(String sql) throws BusinessException {
        return this.search(sql, new ArrayList<Object>(), KeyFormatter.LOWERCASE);
    }
    
    public List<Map<String, String>> search(String sql, KeyFormatter formatter) throws BusinessException {
        return this.search(sql, new ArrayList<Object>(), formatter);
    }
    
    public List<Map<String, String>> search(String sql, Object para) throws BusinessException {
        return this.search(sql, para, KeyFormatter.LOWERCASE);
    }

    public List<Map<String, String>> search(String sql, Object para, KeyFormatter formatter) throws BusinessException {
        List<Object> paras = Arrays.asList(para);
        return this.search(sql, paras, formatter);
    }

    public List<Map<String, String>> search(String sql, List<Object> paras) throws BusinessException {
        return this.search(sql, paras, KeyFormatter.LOWERCASE);
    }
    
    @SuppressWarnings("unchecked")
    public List<Map<String, String>> search(String sql, List<Object> paras, KeyFormatter formatter) throws BusinessException {
        try {
            String querySql = DaoHelper.getQuerySql(sql);
            Query query = entityManager.createNativeQuery(querySql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            DaoHelper.setQueryParas(query, paras);
            List<Map<String, String>> resultList = new LinkedList<>();
            List<Map<String, Object>> list = query.getResultList();
            for (Map<String, Object> map : list) {
                Map<String, String> resultMap = new HashMap<String, String>();
                Set<String> key = map.keySet();
                for (Iterator<String> it = key.iterator(); it.hasNext(); ) {
                    String s = it.next();
                    resultMap.put(DaoHelper.keyFormatConverter(s, formatter), DaoHelper.objectDataToString(map.get(s)));
                } 
                resultList.add(resultMap);
            }
            return resultList;
        } catch (Exception e) {
            throw new BusinessException("[JDBC] Error in executing SQL statement ...", e);
        } 
    }

    public List<Map<String, String>> search(String sql, Page page) throws BusinessException {
        return this.search(sql, null, page, KeyFormatter.LOWERCASE);
    }
    
    public List<Map<String, String>> search(String sql, Page page, KeyFormatter formatter) throws BusinessException {
        return this.search(sql, null, page, formatter);
    }
    
    public List<Map<String, String>> search(String sql, Object para, Page page) throws BusinessException {
        return this.search(sql, para, page, KeyFormatter.LOWERCASE);
    }

    public List<Map<String, String>> search(String sql, Object para, Page page, KeyFormatter formatter) throws BusinessException {
        List<Object> paras = Arrays.asList(para);
        return this.search(sql, paras, page, formatter);
    }
    
    public List<Map<String, String>> search(String sql, List<Object> paras, Page page) throws BusinessException {
        return this.search(sql, paras, page, KeyFormatter.LOWERCASE);
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, String>> search(String sql, List<Object> paras, Page page, KeyFormatter formatter) throws BusinessException {
        if(null == page){
            page = new Page();
        }
        //避免深度分页查询
        if(page.getPageNumber() > PAGE_MAX_DEPTH) {
            return Collections.EMPTY_LIST;
        }
        int count = this.count(DaoHelper.getQueryCountSql(sql), paras);
        page.setTotalRow(count);
        if(count <= 0) {
            return Collections.EMPTY_LIST;
        }
        try {
            String querySql = DaoHelper.getQuerySql(sql);
            Query query = entityManager.createNativeQuery(querySql)
                    .setFirstResult(page.getFromIndex()).setMaxResults(page.getPageSize())
                    .unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            DaoHelper.setQueryParas(query, paras);
            List<Map<String, Object>> list = query.getResultList();
            List<Map<String, String>> resultList = new LinkedList<Map<String, String>>();
            for (Map<String, Object> map : list) {
                Map<String, String> resultMap = new HashMap<>();
                Set<String> key = map.keySet();
                for (Iterator<String> it = key.iterator(); it.hasNext(); ) {
                    String s = it.next();
                    resultMap.put(DaoHelper.keyFormatConverter(s, formatter), DaoHelper.objectDataToString(map.get(s)));
                } 
                resultList.add(resultMap);
            } 
            return resultList;
        } catch (Exception e) {
            throw new BusinessException("[JDBC] Error in executing SQL statement ...", e);
        } 
    }

    public Map<String, String> find(String sql) throws BusinessException {
        return this.find(sql, new ArrayList<Object>(), KeyFormatter.LOWERCASE);
    }
    
    public Map<String, String> find(String sql, KeyFormatter formatter) throws BusinessException {
        return this.find(sql, new ArrayList<Object>(), formatter);
    }

    public Map<String, String> find(String sql, Object para) throws BusinessException {
        return this.find(sql, para, KeyFormatter.LOWERCASE);
    }
    
    public Map<String, String> find(String sql, Object para, KeyFormatter formatter) throws BusinessException {
        List<Object> paras = Arrays.asList(para);
        return this.find(sql, paras, formatter);
    }

    public Map<String, String> find(String sql, List<Object> paras) throws BusinessException {
        return this.find(sql, paras, KeyFormatter.LOWERCASE);
    }
    
    @SuppressWarnings({ "unchecked" })
    public Map<String, String> find(String sql, List<Object> paras, KeyFormatter formatter) throws BusinessException {
        try {
            String querySql = DaoHelper.getQuerySql(sql);
            Query query = entityManager.createNativeQuery(querySql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            DaoHelper.setQueryParas(query, paras);
            List<Map<String, Object>> list = query.getResultList();
            Map<String, String> resultMap = new HashMap<String, String>();
            for (Map<String, Object> map : list) {
                Set<String> key = map.keySet();
                for (Iterator<String> it = key.iterator(); it.hasNext(); ) {
                    String s = it.next();
                    resultMap.put(DaoHelper.keyFormatConverter(s, formatter), DaoHelper.objectDataToString(map.get(s)));
                }
                break;
            }
            return resultMap;
        } catch (Exception e) {
            throw new BusinessException("[JDBC] Error in executing SQL statement ...", e);
        } 
    }

    public int count(String sql) throws BusinessException {
        return this.count(sql, null);
    }

    public int count(String sql, Object para) throws BusinessException {
        List<Object> paras = new ArrayList<Object>();
        paras.add(para);
        return this.count(sql, null);
    }

    @SuppressWarnings({ "unchecked" })
    public int count(String sql, List<Object> paras) throws BusinessException {
        int result = 0;
        try {
            String querySql = DaoHelper.getQuerySql(sql);
            Query query = entityManager.createNativeQuery(querySql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            DaoHelper.setQueryParas(query, paras);
            List<Map<String, Object>> list = query.getResultList();
            for (Map<String, Object> map : list) {
                Set<String> key = map.keySet();
                for (Iterator<String> it = key.iterator(); it.hasNext(); ) {
                    String s = it.next();
                    result = Integer.parseInt(DaoHelper.objectDataToString(map.get(s)));
                } 
            } 
        } catch (Exception e) {
            throw new BusinessException("[JDBC] Error in executing SQL statement ...", e);
        } 
        return result;
    }
}
